package uqbar.comm.simpleProtocol;

import java.io.IOException;
import java.io.InputStream;

import uqbar.comm.ProgramException;
import uqbar.comm.protocol.InputBuilder;
import uqbar.comm.protocol.InputFactory;
import uqbar.comm.protocol.InputProcessor;


public class SimpleInputBuilder implements InputBuilder {

	private String type;
	private int readed;
	private int size;
	private byte[] buffer;
	private boolean reading = false;

	@Override
	public synchronized <T> void read(final InputStream inputStream, final InputFactory<T> inputFactory,
			final InputProcessor<T> inputProcessor) {
		if (!this.reading && !this.disponible(inputStream)) {
			return;
		}
		if (!this.reading) {
			this.start(inputStream);
		}
		this.readContent(inputStream);
		if (this.complete()) {
			inputProcessor.process(inputFactory.create(this.type, this.buffer));
			this.reading = false;
		}
		if (this.disponible(inputStream)) {
			this.read(inputStream, inputFactory, inputProcessor);
		}
	}

	private boolean complete() {
		return this.readed == this.size;
	}

	private void readContent(final InputStream input) {
		try {
			while (input.available() > 0 && this.readed < this.size) {
				this.readed += input.read(this.buffer, this.readed, this.buffer.length - this.readed);
			}
		} catch (IOException e) {
			throw ProgramException.wrap(e);
		}
	}

	private boolean disponible(final InputStream input) {
		try {
			return input.available() >= 7;
		} catch (IOException e) {
			throw ProgramException.wrap(e);
		}
	}

	private void start(final InputStream input) {
		this.reading = true;
		this.readed = 0;
		this.type = this.readType(input);
		this.size = this.readSize(input);
		this.buffer = new byte[this.size];
	}

	private String readType(final InputStream input) {
		byte[] buffer = new byte[2];
		try {
			input.read(buffer);
		} catch (IOException e) {
			throw ProgramException.wrap(e);
		}
		return new String(buffer);
	}

	private int readSize(final InputStream input) {
		byte[] buffer = new byte[5];
		try {
			input.read(buffer);
		} catch (IOException e) {
			throw ProgramException.wrap(e);
		}
		return new Integer(new String(buffer));
	}

}